#include "ChipAwareContainer.h"

__thread int ChipAwareContainer::exitsNum = 0;
__thread bool ChipAwareContainer::hasRandomSeed = false;
__thread int ChipAwareContainer::consumerId = -1;
__thread int ChipAwareContainer::producerId = -1;

ChipAwareContainer::ChipAwareContainer(int maxConsumersPerChip){
	chipsNum = Config::cpus->chipNum;
	chips = new Chip*[chipsNum];
	chipRegistrations = new int[chipsNum];
	for(int i = 0; i < chipsNum; i++)
	{
		chips[i] = new Chip(Config::perChipContainerType, Config::perChipContainersNum, maxConsumersPerChip);
		chipRegistrations[i] = 0;
	}
}

Task* ChipAwareContainer::get(Statistics* stat){
	if(consumerId < 0)  // consumer not registered
	{
		return NULL;
	}
	// find consumer's chip
	int chipId = Config::consumerToChip->find(consumerId)->second;
	Chip* chip = chips[chipId];
	
	// first try to get a task from thread's chip
	Task* task = getFromChip(chip,stat);
	if(task != NULL) return task;
	
	if(!Config::cpus->crossChipWorkStealing)
	{
		return NULL;
	}
	
	// if getting a task from local chip failed, traverse all chips, starting from a random chip
	if(!hasRandomSeed)
	{
		TaskContainer::generateRandomSeed();
		hasRandomSeed = true;
	}
	int randStart = rand() % chipsNum;
	for(int i = 0; i < chipsNum; i++)
	{
		int choice = (i + randStart) % chipsNum;
		if(choice == chipId || chips[choice] == NULL) continue;
		exitsNum++;
		task = getFromChip(chips[choice],stat);
		if(task != NULL) return task;		
	}
	return NULL;
}


Task* ChipAwareContainer::getFromChip(Chip* chip, Statistics* stat){
	if(chip == NULL)  // shouldn't happen..
	{
		cout << "NULL chip" << endl;
		return NULL;
	}
	TaskContainer* container = chip->getRandomContainer();
	if(container == NULL)  // shouldn't happen
	{
		cout << "NULL initial container" << endl;
		return NULL;
	}
	Task* task = container->get(stat);
	if(task != NULL)
	{
		return task;
	}
	container = chip->getNextContainer();
	while(container != NULL)
	{
		task = container->get(stat);
		if(task != NULL)
		{
			return task;
		}
		container = chip->getNextContainer();
	}
	return NULL;
}


void ChipAwareContainer::put(Task* task, Statistics* stat){
	if(producerId < 0)
	{
		return;
	}
	int chipId = Config::producerToChip->find(producerId)->second;
	TaskContainer* container = chips[chipId]->getRandomContainer();
	container->put(task,stat);
}
	

void ChipAwareContainer::registerConsumer(int tid){
	consumerId = tid;
	int chipId = Config::consumerToChip->find(consumerId)->second;
	chips[chipId]->registerConsumer(tid);
	__sync_fetch_and_add(&chipRegistrations[chipId],1);
}


void ChipAwareContainer::registerProducer(int tid){
	producerId = tid;
	int chipId = Config::producerToChip->find(producerId)->second;
	if(chips[chipId] != NULL)
	{
		chips[chipId]->registerProducer(tid);
	}
}

/* 
	a problem with this function (also in java implementation) - the rule of having at least one producer and one consumer at each chip is not enforced.
	deleting chips with no consumers will lead to illegal memory access attempts by producers in producer-only groups.
*/
void ChipAwareContainer::init(){
/*
	int* chipConsCount = new int[chipsNum];
	for(int i = 0; i < chipsNum; i++)
	{
		chipConsCount[i] = 0;
	}
	for(int i = 0; i < Config::cpus->consCoreNum; i++)
	{
		int chipId = Config::getChipByCoreId(Config::cpus->cons[i]);
		chipConsCount[chipId]++;
	}
	for(int i = 0; i < chipsNum; i++)
	{
		if(chipConsCount[i] == 0)
		{
			delete chips[i];
			chips[i] = NULL;
		}
		else
		{
			//chips[i]->setConsumersNum(chipConsCount[i]);
		}
	}
	delete[] chipConsCount;
*/
}

ChipAwareContainer::~ChipAwareContainer(){
	delete[] chipRegistrations;
	for(int i = 0; i < chipsNum; i++)
	{
		delete chips[i];
	}
	delete[] chips;
}


	